package com.sen.gateway.auth;

import com.sen.common.common.response.ResultMsgEnum;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import reactor.core.publisher.Mono;

/**
 * @author esjiang
 */
public class CustomAuthenticationManager implements ReactiveAuthenticationManager {
    private TokenStore tokenStore;

    public CustomAuthenticationManager(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                .filter(a -> a instanceof BearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                .map(BearerTokenAuthenticationToken::getToken)
                .flatMap((accessTokenValue -> {
                    OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
                    if (accessToken == null) {
                        return Mono.error(new OAuth2AuthenticationException(new OAuth2Error(String.valueOf(ResultMsgEnum.INVALID_TOKEN.getCode()),
                                ResultMsgEnum.INVALID_TOKEN.getMsg(),null)));
                    } else if (accessToken.isExpired()) {
                        tokenStore.removeAccessToken(accessToken);
                        return Mono.error(new OAuth2AuthenticationException(new OAuth2Error(String.valueOf(ResultMsgEnum.TOKEN_EXPIRED.getCode()),
                                ResultMsgEnum.TOKEN_EXPIRED.getMsg(),null)));
                    }

                    OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
                    if (result == null) {
                        return Mono.error(new OAuth2AuthenticationException(new OAuth2Error(String.valueOf(ResultMsgEnum.INVALID_TOKEN.getCode()),
                                ResultMsgEnum.INVALID_TOKEN.getMsg(),null)));
                    }
                    return Mono.just(result);
                }))
                .cast(Authentication.class);
    }
}
